<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="林湃滨的个人博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    【Redis】Redis简介 |  滨书
  </title>
  <meta name="generator" content="hexo-theme-yilia-plus">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/css/style.css">

  
<script src="/js/pace.min.js"></script>


  

  

<link rel="alternate" href="/atom.xml" title="滨书" type="application/atom+xml">
</head>

</html>

<body>
  <div id="app">
    <main class="content">
      <section class="outer">
  <article id="post-Redis/【Redis】Redis简介" class="article article-type-post" itemscope
  itemprop="blogPost" data-scroll-reveal>

  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  【Redis】Redis简介
</h1>
  

    </header>
    

    
    <div class="article-meta">
      <a href="/2019/10/21/Redis/%E3%80%90Redis%E3%80%91Redis%E7%AE%80%E4%BB%8B/" class="article-date">
  <time datetime="2019-10-21T13:54:50.000Z" itemprop="datePublished">2019-10-21</time>
</a>
      
  <div class="article-category">
    <a class="article-category-link" href="/categories/Redis%E5%9F%BA%E7%A1%80/">Redis基础</a>
  </div>

      
      
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">2.1k字</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">7分钟</span>
        </span>
    </span>
</div>

      
    </div>
    

    
    
    <div class="tocbot"></div>





    

    <div class="article-entry" itemprop="articleBody">
      


      

      
      <h1 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h1><h2 id="Redis简介"><a href="#Redis简介" class="headerlink" title="Redis简介"></a>Redis简介</h2><p>Redis 是完全开源免费的，遵守 BSD 协议，是一个高性能的 key - value 数据库</p>
<p>Redis 与 其他 key - value 缓存产品有以下三个特点：</p>
<ul>
<li>Redis 支持数据持久化，可以将内存中的数据保存在磁盘中，重启的时候可以再次加载进行使用。</li>
<li>Redis 不仅仅支持简单的 key - value 类型的数据，同时还提供 list，set，zset，hash 等数据结构的存储</li>
<li>Redis 支持数据的备份，即 master - slave 模式的数据备份</li>
</ul>
<h2 id="Redis数据类型"><a href="#Redis数据类型" class="headerlink" title="Redis数据类型"></a>Redis数据类型</h2><table>
<thead>
<tr>
<th align="center">结构类型</th>
<th align="center">结构存储的值</th>
<th align="center">读写能力</th>
</tr>
</thead>
<tbody><tr>
<td align="center">String</td>
<td align="center">可以是字符串，整数或者浮点数</td>
<td align="center">可以对整个字符串或者字符串中的一部分进行操作，对整数和浮点数执行自增或者自减操作</td>
</tr>
<tr>
<td align="center">List</td>
<td align="center">一个链表，链表上的每个节点都包含一个字符串</td>
<td align="center">从链表两端添加元素或者弹出元素，<strong>根据偏移量对链表进行修剪</strong>；读取单个或者多个元素，根据值查找或溢出元素</td>
</tr>
<tr>
<td align="center">Set</td>
<td align="center">包含字符串的无序集合，且其中的每个字符串都是各不相同的</td>
<td align="center">添加、获取、删除单个元素，检查一个元素是否存在于集合中，计算交集、并集、差集；从集合中随机获取元素</td>
</tr>
<tr>
<td align="center">Hash</td>
<td align="center">包含键值对的无序哈希表</td>
<td align="center">添加、获取、删除单个键值对，获取所有键值对</td>
</tr>
<tr>
<td align="center">Zset</td>
<td align="center">字符串成员与浮点数分支之间的有序映射，元素的排列顺序由分值的大小决定</td>
<td align="center">添加、获取、删除单个元素，根据分支范围（range）或者成员来获取元素。</td>
</tr>
</tbody></table>
<h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>redis事务一次可以执行多条命令，服务器在执行命令期间，不会执行其他客户端的命令请求。</p>
<p>事务中的一条或多条命令被一次性发送到服务器，并不是逐条发送，此种方式被称为流水线，可以减少客户端与服务器之间的网络通信次数，提升性能。</p>
<p>Redis一般的事务实现方式：使用MULTI和EXEC命令将事务操作包起来：</p>
<ol>
<li>批量操作在发送EXEC命令前被放入队列缓存；</li>
<li>收到EXEC命令后进入事务执行，事务中任意命令执行失败，其余命令依然被执行。也就是说Redis事务不保证原子性；</li>
<li>在事务执行过程中，其他客户端提交的命令不会插入到事务执行命令序列中；</li>
</ol>
<p>一个事务从开始到执行过程中经历的三个阶段：</p>
<ul>
<li>开始事务；</li>
<li>命令入队；</li>
<li>执行事务；</li>
</ul>
<p>eg：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">MULTI	<span class="comment">--1、开始事务</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--2、命令入队</span></span><br><span class="line"><span class="keyword">SET</span> book-<span class="keyword">name</span> <span class="string">"Mastering C++ in 21 days"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">Get</span> book-<span class="keyword">name</span></span><br><span class="line"></span><br><span class="line">SADD tag <span class="string">"C++"</span> <span class="string">"Programming"</span> <span class="string">"Mastering Series"</span></span><br><span class="line"></span><br><span class="line">SMEMBERS tag</span><br><span class="line"></span><br><span class="line">EXEC	<span class="comment">--3、执行事务</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>单个命令的执行是原子性的，但是Redis没有在事务上增加任何维持原子性的机制，所以Redis事务的执行并不是原子性的。</p>
<p>Redis的事务，可以理解是一个打包好的、批量的执行脚本，但是这些批量的指令并不是原子化的操作，中间某条指令的失败，不会导致事务中之前执行的指令的回滚，也不会导致之后的指令不会执行。</p>
<p>官网相关内容说明：</p>
<p> <em>It’s important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands</em> </p>
</blockquote>
<h3 id="Redis事务命令"><a href="#Redis事务命令" class="headerlink" title="Redis事务命令"></a>Redis事务命令</h3><table>
<thead>
<tr>
<th align="center">命令</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">DISCARD</td>
<td align="center">取消事务，放弃执行事务块内的所有命令</td>
</tr>
<tr>
<td align="center">EXEC</td>
<td align="center">执行所有事务块内的命令</td>
</tr>
<tr>
<td align="center">MULTI</td>
<td align="center">标记事务块的开始</td>
</tr>
<tr>
<td align="center">UNWATCH</td>
<td align="center">取消Watch命令对所有key的监视</td>
</tr>
<tr>
<td align="center">WATCH key [key …]</td>
<td align="center">监视一个/多个key，如果在事务执行之前这个/些 key 被其他命令改动<br>则该事务将被打断</td>
</tr>
</tbody></table>
<h2 id="Redis-持久化"><a href="#Redis-持久化" class="headerlink" title="Redis 持久化"></a>Redis 持久化</h2><p>Redis 是内存型数据库，为了保证数据在断电后不会丢失，需要将内存中的数据持久化到硬盘上。</p>
<h3 id="（1）RDB-持久化"><a href="#（1）RDB-持久化" class="headerlink" title="（1）RDB 持久化"></a>（1）RDB 持久化</h3><p>将某个时间点的所有数据都存放到硬盘上。</p>
<p>可以将快照复制到其他服务器从而创建具有相同数据的服务器副本。</p>
<p>如果系统发生故障，将会丢失最后一次创建快照之后的数据。</p>
<p>如果数据量大，保存快照的时间会很长。</p>
<h3 id="（2）AOF-持久化"><a href="#（2）AOF-持久化" class="headerlink" title="（2）AOF 持久化"></a>（2）AOF 持久化</h3><p>将写命令添加到 AOF 文件（append only file）末尾。</p>
<p>使用 AOF 持久化需要设置同步选项，从而确保<strong>写命令</strong>同步到磁盘文件上的时机。</p>
<p>这是因为对文件进行写入并不会马上将内容同步到磁盘上，而是先存储到缓冲区，然后由操作系统决定什么时候同步到磁盘。</p>
<p>选项同步频率always每个写命令都同步eyerysec每秒同步一次no让操作系统来决定何时同步</p>
<ul>
<li>always 选项会严重减低服务器的性能</li>
<li>everysec 选项比较合适，可以保证系统崩溃时只会丢失一秒左右的数据，并且 Redis 每秒执行一次同步对服务器几乎没有任何影响。</li>
<li>no 选项并不能给服务器性能带来多大的提升，而且会增加系统崩溃时数据丢失的数量。</li>
</ul>
<p>随着服务器写请求的增多，AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性，能够去除 AOF 文件中的冗余写命令。</p>
<h2 id="复制"><a href="#复制" class="headerlink" title="复制"></a>复制</h2><p>通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。</p>
<p>一个从服务器只能有一个主服务器，并且不支持主主复制。</p>
<h3 id="连接过程"><a href="#连接过程" class="headerlink" title="连接过程"></a>连接过程</h3><ol>
<li>主服务器创建快照文件，即 RDB 文件，发送给从服务器，并在发送期间使用缓冲区记录执行的写命令。</li>
<li>快照文件发送完毕之后，开始像从服务器发送存储在缓冲区的写命令。</li>
<li>从服务器丢弃所有旧数据，载入主服务器发来的快照文件，之后从服务器开始接受主服务器发来的写命令。</li>
<li>主服务器每执行一次写命令，就向从服务器发送相同的写命令。</li>
</ol>
<h3 id="主从链"><a href="#主从链" class="headerlink" title="主从链"></a>主从链</h3><p>随着负载不断上升，主服务器无法很快的更新所有从服务器，或者重新连接和重新同步从服务器将导致系统超载。</p>
<p>为了解决这个问题，可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器，又是最下层服务器的主服务器。</p>
<h2 id="哨兵"><a href="#哨兵" class="headerlink" title="哨兵"></a>哨兵</h2><p>Sentinel（哨兵）可以监听集群中的服务器，并在主服务器进入下线状态时，自动从从服务器中选举处新的主服务器。</p>
<h2 id="分片"><a href="#分片" class="headerlink" title="分片"></a>分片</h2><p>分片是将数据划分为多个部分的方法，可以将数据存储到多台机器里面，这种方法在解决某些问题时可以获得线性级别的性能提升。</p>
<p>假设有 4 个 Redis 实例 R0, R1, R2, R3, 还有很多表示用户的键 user:1, user:2, … , 有不同的方式来选择一个指定的键存储在哪个实例中。</p>
<ul>
<li>最简单的是范围分片，例如用户 id 从 0 ~ 1000 的存储到实例 R0 中，用户 id 从 1001 ~ 2000 的存储到实例 R1中，等等。但是这样需要维护一张映射范围表，维护操作代价高。</li>
<li>还有一种是哈希分片。使用 CRC32 哈希函数将键转换为一个数字，再对实例数量求模就能知道存储的实例。</li>
</ul>
<p>根据执行分片的位置，可以分为三种分片方式：</p>
<ul>
<li>客户端分片：客户端使用一致性哈希等算法决定应当分布到哪个节点。</li>
<li>代理分片：将客户端的请求发送到代理上，由代理转发到正确的节点上。</li>
<li>服务器分片：Redis Cluster。</li>
</ul>

      
      <!-- reward -->
      
      <div id="reward-btn">
        打赏
      </div>
      
    </div>
      <!-- copyright -->
      
        <div class="declare">
          <ul class="post-copyright">
            <li>
              <i class="ri-copyright-line"></i>
              <strong>版权声明： </strong s>
              本博客所有文章除特别声明外，均采用 <a href="https://www.apache.org/licenses/LICENSE-2.0.html" rel="external nofollow"
                target="_blank">Apache License 2.0</a> 许可协议。转载请注明出处！
            </li>
          </ul>
        </div>
        
    <footer class="article-footer">
      
          
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://linpaibin.gitee.io/2019/10/21/Redis/%E3%80%90Redis%E3%80%91Redis%E7%AE%80%E4%BB%8B/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Redis/" rel="tag">Redis</a></li></ul>


    </footer>

  </div>

  
  
  <nav class="article-nav">
    
      <a href="/2019/10/21/Netty/%E3%80%90Netty%E3%80%91Netty%E7%AE%80%E4%BB%8B/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            【Netty】Netty简介
          
        </div>
      </a>
    
    
      <a href="/2019/10/19/DistributedArchitecture/%E5%88%86%E5%B8%83%E5%BC%8F%E6%9E%B6%E6%9E%84%E4%BD%93%E7%B3%BB/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">分布式架构体系</div>
      </a>
    
  </nav>


  

  
  
<!-- valine评论 -->
<div id="vcomments-box">
    <div id="vcomments">
    </div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src='https://cdn.jsdelivr.net/npm/valine@1.3.10/dist/Valine.min.js'></script>
<script>
    new Valine({
        el: '#vcomments',
        notify: false,
        verify: '',
        app_id: 'gtXNe43HVDH72a7XMn9zNgz8-gzGzoHsz',
        app_key: 'Df8WdcqcC2DCWL1pfcWb4QfN',
        path: window.location.pathname,
        avatar: 'mp',
        placeholder: '既然都来了，便留个爪印呗~_(¦3」∠)_',
        recordIP: true
    });
    const infoEle = document.querySelector('#vcomments .info');
    if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
        infoEle.childNodes.forEach(function (item) {
            item.parentNode.removeChild(item);
        });
    }
</script>
<style>
    #vcomments-box {
        padding: 5px 30px;
    }

    @media screen and (max-width: 800px) {
        #vcomments-box {
            padding: 5px 0px;
        }
    }

    #vcomments-box #vcomments {
        background-color: #fff;
    }

    .v .vlist .vcard .vh {
        padding-right: 20px;
    }

    .v .vlist .vcard {
        padding-left: 10px;
    }
</style>

  

  
  
  

</article>
</section>
      <footer class="footer">
  <div class="outer">
    <ul class="list-inline">
      <li>
        &copy;
        2019-2020
        LinPaiBin
      </li>
      <li>
        
          Powered by
        
        
        <a href="https://hexo.io" target="_blank">Hexo</a> Theme <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul class="list-inline">
      <li>
        
        
        <span>
  <i>PV:<span id="busuanzi_value_page_pv"></span></i>
  <i>UV:<span id="busuanzi_value_site_uv"></span></i>
</span>
        
      </li>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
    <div class="to_top">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>
      </div>
    </main>
      <aside class="sidebar">
        <button class="navbar-toggle"></button>
<nav class="navbar" style="opacity: 0.5;">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="滨书"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E7%94%9F%E6%B4%BB/">生活</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/12/02/AboutMe">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
      </aside>
      <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
      
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/jquery.justifiedGallery.min.js"></script>


<script src="/js/lazyload.min.js"></script>


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<script src="/js/share.js"></script>



<script src="/fancybox/jquery.fancybox.min.js"></script>




<script>
  try {
    var typed = new Typed("#subtitle", {
    strings: ['人生如棋，吾愿为卒','愿你一生努力，一生无憾','不求事事顺意，但求无愧于心'],
    startDelay: 0,
    typeSpeed: 250,
    loop: true,
    backSpeed: 100,
    showCursor: true
    });
  } catch (err) {
  }
  
</script>




<script src="/js/tocbot.min.js"></script>

<script>
  // Tocbot_v4.7.0  http://tscanlin.github.io/tocbot/
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer:'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto',
    onClick: (e) => {
      $('.toc-link').removeClass('is-active-link');
      $(`a[href=${e.target.hash}]`).addClass('is-active-link');
      $(e.target.hash).scrollIntoView();
      return false;
    }
  });
</script>


<script>
  var ayerConfig = {
    mathjax: false
  }
</script>


<script src="/js/ayer.js"></script>


<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">


<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>




<script type="text/javascript" src="https://js.users.51.la/20544303.js"></script>
  </div>
</body>

</html>